#include "preHeader.h"
#include "Session/ClientPacketHandler.h"
#include "Session/CenterSession.h"
#include "Game/AccountMgr.h"
#include "Game/CharacterMgr.h"
#include "Game/TeleportMgr.h"
#include "GameServer.h"
#include "Session/DBPSession.h"

int ClientPacketHandler::HandleCharacterCreate(Account *pAccount, INetPacket &pck)
{
	std::string name;
	uint32 charTypeId;
	uint8 career, gender;
	pck >> name >> charTypeId >> career >> gender;

	GErrorCode err = sCharacterMgr.CheckPlayerName(name);
	if (err != CommonSuccess) {
		pAccount->SendError(err);
		return SessionHandleSuccess;
	}
	const CharPrototype* pProto = GetDBEntry<CharPrototype>(charTypeId);
	if (pProto == NULL || pProto->charRace != (u32)CharRaceType::Player) {
		pAccount->SendError(ParamInvalid);
		return SessionHandleSuccess;
	}

	auto CreateCharacterCallback = [=, uid = pAccount->uid](INetStream& pck) -> GErrorCode {
		int32 errCode;
		std::string_view errMsg;
		pck >> errCode >> errMsg;
		if (errCode != 0) {
			auto pAccount = sAccountMgr.GetAccount(uid);
			if (pAccount != NULL) {
				pAccount->SendError({CommonTransError, errCode, errMsg});
			}
			WLOG("client account`%u` create character, code 1.", uid);
			return CommonSuccess;
		}

		uint32 logicGsID, charId;
		pck >> logicGsID >> charId;
		inst_player_char instInfo;
		instInfo.ipcInstID = charId;
		instInfo.ipcAcctID = uid;
		instInfo.ipcNickName = name;
		instInfo.ipcCareer = career;
		instInfo.ipcGender = gender;
		instInfo.ipcServerID = logicGsID;
		instInfo.ipcAppearance.charTypeId = charTypeId;
		sCharacterMgr.InitPlayerInstance(instInfo);

		NetPacket rpcReqPck(CDBP_NEW_ONE_PLAYER_INSTANCE);
		SaveToINetStream(instInfo, rpcReqPck);
		sDBPSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
			sCharacterMgr.OnCreatePlayerInstance(uid, pck, err);
		}, &sGameServer);

		return CommonSuccess;
	};

	NetPacket rpcReqPck(CCT_CHARACTER_CREATE);
	rpcReqPck << pAccount->logicGsID << pAccount->uid << name;
	sCenterSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
		if (err == RPCErrorNone) {
			CreateCharacterCallback(pck);
		} else {
			WLOG("CenterSession.RPCInvoke() HandleCharacterCreate error[%d].", err);
		}
	}, &sGameServer, DEF_S2S_RPC_TIMEOUT);
	NLOG("client account`%u` create character`%s` ...", pAccount->uid, name.c_str());

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleCharacterDelete(Account *pAccount, INetPacket &pck)
{
	uint32 charId;
	pck >> charId;

	auto pChar = sCharacterMgr.GetCharacter(charId);
	if (pChar == NULL || pChar->acct != pAccount->uid) {
		pAccount->SendError(InvalidRequest);
		return SessionHandleSuccess;
	}

	auto DeleteCharacterCallback = [=, uid = pAccount->uid](INetStream& pck)->GErrorCode {
		int32 errCode;
		std::string_view errMsg;
		pck >> errCode >> errMsg;
		if (errCode != 0) {
			auto pAccount = sAccountMgr.GetAccount(uid);
			if (pAccount != NULL) {
				pAccount->SendError({CommonTransError, errCode, errMsg});
			}
			WLOG("client account`%u` delete character, code 1.", uid);
			return CommonSuccess;
		}

		NetPacket rpcReqPck(CDBP_DELETE_ONE_PLAYER_INSTANCE);
		rpcReqPck << charId;
		sDBPSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
			sCharacterMgr.OnDeletePlayerInstance(uid, pck, err);
		}, &sGameServer);

		return CommonSuccess;
	};

	NetPacket rpcReqPck(CCT_CHARACTER_DELETE);
	rpcReqPck << charId;
	sCenterSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
		if (err == RPCErrorNone) {
			DeleteCharacterCallback(pck);
		} else {
			WLOG("CenterSession.RPCInvoke() HandleCharacterDelete error[%d].", err);
		}
	}, &sGameServer, DEF_S2S_RPC_TIMEOUT);
	NLOG("client account`%u` delete character`%u` ...", pAccount->uid, charId);

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleCharacterList(Account *pAccount, INetPacket &pck)
{
	NetPacket rpcReqPck(CDBP_LOAD_ACCOUNT_PLAYER_PREVIEW_INFO);
	rpcReqPck << pAccount->uid << pAccount->logicGsID;
	sDBPSession.RPCInvoke(rpcReqPck,
		[uid = pAccount->uid](INetStream& pck, int32 err, bool) {
		if (err != RPCErrorNone) {
			WLOG("HandleCharacterList[%u] failed, %d.", uid, err);
			return;
		}
		auto pAccount = sAccountMgr.GetAccount(uid);
		if (pAccount == NULL) {
			WLOG("HandleCharacterList[%u] failed, null.", uid);
			return;
		}
		NetPacket resp(SMSG_CHARACTER_LIST_RESP);
		resp.Append(pck.GetReadableBuffer(), pck.GetReadableSize());
		pAccount->SendPacket(resp);
		NLOG("client account`%u` get character list successfully.", uid);
	}, &sGameServer);
	NLOG("client account`%u` get character list ...", pAccount->uid);
	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleCharacterLogin(Account *pAccount, INetPacket &pck)
{
	if (pAccount->IsCharacterOnline()) {
		Character* pChar = pAccount->GetCharacter();
		WLOG("HandleCharacterLogin[%u]: already login charcter[%u,%s]!",
			pAccount->uid, pChar->guid.UID, pChar->name.c_str());
		pAccount->SendRespError(
			SMSG_CHARACTER_LOGIN_RESP, LOGIN_ERROR_CHARACTER_ONLINE);
		return SessionHandleSuccess;
	}

	if (pAccount->isCharLoginning) {
		WLOG("HandleCharacterLogin[%u]: charcter is loginning ...",
			pAccount->uid);
		pAccount->SendRespError(
			SMSG_CHARACTER_LOGIN_RESP, LOGIN_ERROR_CHARACTER_LOGINNING);
		return SessionHandleSuccess;
	}

	uint32 charId;
	pck >> charId;

	const uint32 sn = pAccount->sn;
	const uint32 uid = pAccount->uid;
	pAccount->isCharLoginning = true;

	NetPacket rpcReqPck(CDBP_LOAD_ONE_PLAYER_CHARACTER_INFO);
	rpcReqPck << charId;
	sDBPSession.RPCInvoke(rpcReqPck, [=](INetStream& pck, int32 err, bool) {
		Account* pAccount = sAccountMgr.GetAccount(uid);
		if (pAccount != NULL && pAccount->sn == sn && pAccount->isCharLoginning) {
			pAccount->isCharLoginning = false;
		} else {
			WLOG("HandleCharacterLogin[%u->%u] failed, account status invalid.",
				pAccount->uid, charId);
			return;
		}
		if (err != RPCErrorNone) {
			WLOG("HandleCharacterLogin[%u->%u] failed, rpc error %d.",
				pAccount->uid, charId, err);
			pAccount->SendRespError(
				SMSG_CHARACTER_LOGIN_RESP, CommonInternalError);
			return;
		}
		if (pck.IsReadableEmpty()) {
			pAccount->SendRespError(
				SMSG_CHARACTER_LOGIN_RESP, LOGIN_ERROR_CHARACTER_NOT_EXSIT);
			WLOG("HandleCharacterLogin[%u->%u] failed, packet is empty.",
				pAccount->uid, charId);
			return;
		}
		InstPlayerOutlineInfo instInfo;
		LoadFromINetStream(instInfo, pck);
		GErrorCode errCode = sCharacterMgr.UpdateCharacter(instInfo);
		if (errCode != CommonSuccess) {
			pAccount->SendRespError(SMSG_CHARACTER_LOGIN_RESP, errCode);
			WLOG("HandleCharacterLogin[%u->%u] failed, error %d.",
				pAccount->uid, charId, errCode);
			return;
		}
		ObjGUID playerGuid = GetGuidFromLowGuid4Player(charId);
		Character* pChar = sCharacterMgr.GetCharacter(playerGuid);
		if (pChar == NULL) {
			pAccount->SendRespError(
				SMSG_CHARACTER_LOGIN_RESP, CommonInternalError);
			WLOG("HandleCharacterLogin[%u->%u] failed, null.",
				pAccount->uid, charId);
			return;
		}
		pAccount->SendRespError(SMSG_CHARACTER_LOGIN_RESP, CommonSuccess);
		errCode = sCharacterMgr.LoginCharacter(pAccount, pChar);
		if (errCode == CommonSuccess) {
			NLOG("client account`%u` login character[%u,%s] successfully.",
				pAccount->uid, charId, pChar->name.c_str());
		} else {
			WLOG("HandleCharacterLogin[%u->%u] failed, error %d.",
				pAccount->uid, charId, err);
		}
	}, &sGameServer);
	NLOG("client account`%u` login character`%u` ...", pAccount->uid, charId);

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandleCharacterLogout(Account *pAccount, INetPacket &pck)
{
	if (!pAccount->IsCharacterOnline()) {
		WLOG("HandleCharacterLogout[%u]: charcter offline.", pAccount->uid);
		pAccount->SendRespError(
			SMSG_CHARACTER_LOGOUT_RESP, LOGIN_ERROR_CHARACTER_OFFLINE);
		return SessionHandleSuccess;
	}

	pAccount->SendRespError(SMSG_CHARACTER_LOGOUT_RESP, CommonSuccess);
	auto errCode = sCharacterMgr.LogoutCharacter(pAccount, pAccount->GetCharacter());
	if (errCode != CommonSuccess) {
		WLOG("HandleCharacterLogout[%u] failed, error %d.", pAccount->uid, errCode);
	}

	return SessionHandleSuccess;
}

int ClientPacketHandler::HandlePlayerLoadMapFinish(Account *pAccount, INetPacket &pck)
{
	auto pChar = pAccount->GetCharacter();
	NLOG("character[%u,%s] load map finish.", pChar->guid.UID, pChar->name.c_str());
	sTeleportMgr.HandlePlayerLoadMapFinish(pChar->guid);
	return SessionHandleSuccess;
}
